{
  lib,
  stdenv,
  version,
  langC,
  langCC,
  langJit,
  enableShared,
  targetPlatform,
  hostPlatform,
  withoutTargetLibc,
  libcCross,
}:

assert !stdenv.targetPlatform.hasSharedLibraries -> !enableShared;

drv:
lib.pipe drv

  (
    [

      (
        pkg:
        pkg.overrideAttrs (
          previousAttrs:
          lib.optionalAttrs
            (
              (!lib.systems.equals targetPlatform hostPlatform)
              && (enableShared || targetPlatform.isMinGW)
              && withoutTargetLibc
            )
            {
              makeFlags = [
                "all-gcc"
                "all-target-libgcc"
              ];
              installTargets = "install-gcc install-target-libgcc";
            }
        )
      )

    ]
    ++ (
      let
        targetPlatformSlash =
          if lib.systems.equals hostPlatform targetPlatform then "" else "${targetPlatform.config}/";

        # If we are building a cross-compiler and the target libc provided
        # to us at build time has a libgcc, use that instead of building a
        # new one.  This avoids having two separate (but identical) libgcc
        # outpaths in the closure of most packages, which can be confusing.
        useLibgccFromTargetLibc = libcCross != null && libcCross ? passthru.libgcc;

        enableLibGccOutput =
          (
            !(stdenv.targetPlatform.isWindows || stdenv.targetPlatform.isCygwin)
            || (lib.systems.equals stdenv.targetPlatform stdenv.hostPlatform)
          )
          && !langJit
          && !stdenv.hostPlatform.isDarwin
          && enableShared
          && !useLibgccFromTargetLibc;

        # For some reason libgcc_s.so has major-version "2" on m68k but
        # "1" everywhere else.  Might be worth changing this to "*".
        libgcc_s-version-major = if targetPlatform.isM68k then "2" else "1";

      in
      [

        (
          pkg:
          pkg.overrideAttrs (
            previousAttrs:
            lib.optionalAttrs useLibgccFromTargetLibc {
              passthru = (previousAttrs.passthru or { }) // {
                inherit (libcCross) libgcc;
              };
            }
          )
        )

        (
          pkg:
          pkg.overrideAttrs (
            previousAttrs:
            lib.optionalAttrs ((!langC) || langJit || enableLibGccOutput) {
              outputs = previousAttrs.outputs ++ lib.optionals enableLibGccOutput [ "libgcc" ];
              # This is a separate phase because gcc assembles its phase scripts
              # in bash instead of nix (we should fix that).
              preFixupPhases =
                (previousAttrs.preFixupPhases or [ ])
                ++ lib.optionals ((!langC) || enableLibGccOutput) [ "preFixupLibGccPhase" ];
              preFixupLibGccPhase =
                # delete extra/unused builds of libgcc_s in non-langC builds
                # (i.e. libgccjit, gnat, etc) to avoid potential confusion
                lib.optionalString (!langC) ''
                  rm -f $out/lib/libgcc_s.so*
                ''

                # move `libgcc_s.so` into its own output, `$libgcc`
                # We maintain $libgcc/lib/$target/ structure to make sure target
                # strip runs over libgcc_s.so and remove debug references to headers:
                #   https://github.com/NixOS/nixpkgs/issues/316114
                + lib.optionalString enableLibGccOutput (
                  ''
                    # move libgcc from lib to its own output (libgcc)
                    mkdir -p $libgcc/${targetPlatformSlash}lib
                    mv    $lib/${targetPlatformSlash}lib/libgcc_s.so      $libgcc/${targetPlatformSlash}lib/
                    mv    $lib/${targetPlatformSlash}lib/libgcc_s.so.${libgcc_s-version-major}    $libgcc/${targetPlatformSlash}lib/
                    ln -s $libgcc/${targetPlatformSlash}lib/libgcc_s.so   $lib/${targetPlatformSlash}lib/
                    ln -s $libgcc/${targetPlatformSlash}lib/libgcc_s.so.${libgcc_s-version-major} $lib/${targetPlatformSlash}lib/
                  ''
                  + lib.optionalString (targetPlatformSlash != "") ''
                    ln -s ${targetPlatformSlash}lib $libgcc/lib
                  ''
                  #
                  # Nixpkgs ordinarily turns dynamic linking into pseudo-static linking:
                  # libraries are still loaded dynamically, exactly which copy of each
                  # library is loaded is permanently fixed at compile time (via RUNPATH).
                  # For libgcc_s we must revert to the "impure dynamic linking" style found
                  # in imperative software distributions.  We must do this because
                  # `libgcc_s` calls `malloc()` and therefore has a `DT_NEEDED` for `libc`,
                  # which creates two problems:
                  #
                  #  1. A circular package dependency `glibc`<-`libgcc`<-`glibc`
                  #
                  #  2. According to the `-Wl,-rpath` flags added by Nixpkgs' `ld-wrapper`,
                  #     the two versions of `glibc` in the cycle above are actually
                  #     different packages.  The later one is compiled by this `gcc`, but
                  #     the earlier one was compiled by the compiler *that compiled* this
                  #     `gcc` (usually the bootstrapFiles).  In any event, the `glibc`
                  #     dynamic loader won't honor that specificity without namespaced
                  #     manual loads (`dlmopen()`).  Once a `libc` is present in the address
                  #     space of a process, that `libc` will be used to satisfy all
                  #     `DT_NEEDED`s for `libc`, regardless of `RUNPATH`s.
                  #
                  # So we wipe the RUNPATH using `patchelf --set-rpath ""`.  We can't use
                  # `patchelf --remove-rpath`, because at least as of patchelf 0.15.0 it
                  # will leave the old RUNPATH string in the file where the reference
                  # scanner can still find it:
                  #
                  #   https://github.com/NixOS/patchelf/issues/453
                  #
                  # Note: we might be using the bootstrapFiles' copy of patchelf, so we have
                  # to keep doing it this way until both the issue is fixed *and* all the
                  # bootstrapFiles are regenerated, on every platform.
                  #
                  # This patchelfing is *not* effectively equivalent to copying
                  # `libgcc_s` into `glibc`'s outpath.  There is one minor and one
                  # major difference:
                  #
                  # 1. (Minor): multiple builds of `glibc` (say, with different
                  #    overrides or parameters) will all reference a single store
                  #    path:
                  #
                  #      /nix/store/xxx...xxx-gcc-libgcc/lib/libgcc_s.so.1
                  #
                  #    This many-to-one referrer relationship will be visible in the store's
                  #    dependency graph, and will be available to `nix-store -q` queries.
                  #    Copying `libgcc_s` into each of its referrers would lose that
                  #    information.
                  #
                  # 2. (Major): by referencing `libgcc_s.so.1`, rather than copying it, we
                  #    are still able to run `nix-store -qd` on it to find out how it got
                  #    built!  Most importantly, we can see from that deriver which compiler
                  #    was used to build it (or if it is part of the unpacked
                  #    bootstrap-files).  Copying `libgcc_s.so.1` from one outpath to
                  #    another eliminates the ability to make these queries.
                  #
                  + ''
                    patchelf --set-rpath "" $libgcc/lib/libgcc_s.so.${libgcc_s-version-major}
                  ''
                );
            }
          )
        )
      ]
    )
  )
